package vacuumCleaner;

import graphUtility.test2.GraphFactory2;

import java.util.Collections;
import java.util.LinkedList;

import vacuumCleaner.Square.Type;


/**
 * Agent function for the vacuum-cleaner environment
 * @see Environment
 */



public class Agent extends AbstractAgent {
	
	private GraphFactory2 factory;
	private Floor discoveredFloor;
	
	public Agent(int x, int y, VisibilityType visType, int opBound){
		super(x, y, visType, opBound);
			switch(visType)
			{
			case MY_NEIGHBOURS:
				//discoveredFloor=new Floor(perception.floor.length,perception.floor.width, null);
				discoveredFloor=new Floor(10,10, Type.UNKNOWN);
			}
		
	}
	

	/**
	 * @return true if the agent has reached the goal
	 */
	public boolean goalReached(){
		return goalReached;
	}
	
	/** 
	 * Make the agent perceives from environment
	 * @param	perception	current perception from the environment
	 */
	public void perceives(Perception perception){
		this.perception = perception;
		// TODO show perception
	}
	
	/**
	 * Move the agent according with the visibility
	 */
	public void update(){
		/*
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		switch (visType) {
		
			case MY_CELL:stupidBehaviour();break;
			case MY_NEIGHBOURS:behaviourMyNeighbours();break;
			case ALL:percorsoGrafo2();break;
		}
	}
	
	/**
	 * Suck the tile if is dirty and moves around randomly if it is clean
	 */
	public void stupidBehaviour(){
		updateGoal();
		System.out.println("MY CELL " + x + "," + y + ": " + perception.floor.get(x,y));
		if(perception.floor.get(x,y) == Square.Type.DIRTY)
			currAction = Action.Type.SUCK;
		else{
			LinkedList<Action.Type> list = new LinkedList<Action.Type>();
			list.add(Action.Type.NORTH);
			list.add(Action.Type.SOUTH);
			list.add(Action.Type.EAST);
			list.add(Action.Type.WEST);
			Collections.shuffle(list);
			currAction = list.getFirst();
		}
	}
	
	boolean primoAvvio=true;
	
	/*
	public void percorsoGrafo()
	{
		updateGoal();
		if(factory.getMossePossibili().isEmpty() && primoAvvio)
		{
			//Passo la percezione corrente per la creazione del grafo locale
			factory.setMyPerception(perception);
			try {
				factory.initBuildGraph();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			factory.searchPath();
			currAction=factory.getMossePossibili().getFirst();
			factory.getMossePossibili().removeFirst();
		}
		else if (!factory.getMossePossibili().isEmpty())
		{
			primoAvvio=false;
			currAction=factory.getMossePossibili().getFirst();
			factory.getMossePossibili().removeFirst();
		}
		else
			currAction=vacuumCleaner.Action.Type.NOOP;
		
	}
	*/
	
	
	public void percorsoGrafo2()
	{
		updateGoal();
		if(factory.getMossePossibili().isEmpty())
		{
			//Passo la percezione corrente per la creazione del grafo locale
			factory.setMyPerception(perception);
			
			factory.initBuildGraph();
			factory.searchPath();
			currAction=factory.getMossePossibili().getFirst();
			factory.getMossePossibili().removeFirst();
		}
		else if (!factory.getMossePossibili().isEmpty())
		{
			currAction=factory.getMossePossibili().getFirst();
			factory.getMossePossibili().removeFirst();
		}
		else
			currAction=vacuumCleaner.Action.Type.NOOP;
		
	}
	
	
	public void behaviourMyNeighbours()
	{
		stupidBehaviour();
		
		for (int i = 0; i < perception.floor.length; i++)
			for (int j = 0; j < perception.floor.width; j++)
				//if(discoveredFloor.get(i, j)==Type.UNKNOWN)
					//discoveredFloor.set(i, j,perception.floor.get(i, j));
			{
				discoveredFloor.set(this.x, this.y, perception.floor.get(this.x, this.y));
				discoveredFloor.set(this.x-1, this.y, perception.floor.get(this.x-1, this.y));
				discoveredFloor.set(this.x+1, this.y, perception.floor.get(this.x+1, this.y));
				discoveredFloor.set(this.x, this.y-1, perception.floor.get(this.x, this.y-1));
				discoveredFloor.set(this.x-1, this.y-1, perception.floor.get(this.x-1, this.y-1));
				discoveredFloor.set(this.x+1, this.y-1, perception.floor.get(this.x+1, this.y-1));
				discoveredFloor.set(this.x, this.y+1, perception.floor.get(this.x, this.y+1));
				discoveredFloor.set(this.x-1, this.y+1, perception.floor.get(this.x-1, this.y+1));
				discoveredFloor.set(this.x+1, this.y+1, perception.floor.get(this.x+1, this.y+1));
			}
		System.out.println(printDiscoveryFloor());
		
	}
	
	public String printDiscoveryFloor()
	{
		StringBuffer sb = new StringBuffer();
		for(int i=0; i<discoveredFloor.length; i++){
			for(int j=0; j<discoveredFloor.width; j++)
				if(this.x == i && this.y == j)
					sb.append("[[=]] ");
				else
					if(discoveredFloor.get(i, j) == Square.Type.DIRTY)
						sb.append("XXXXX ");
				else if(discoveredFloor.get(i, j) == Square.Type.OBSTACLE)
					sb.append("OOOOO ");
				else if(discoveredFloor.get(i, j) == Square.Type.CLEAN)
					sb.append("----- ");
				else
					sb.append("????? ");
			sb.append("\n");
		}
		return sb.toString();
	}
	
	/**
	 * Print the list of agent actions 
	 */
	public void showActions(){
		for(int i=0; i<actionList.size(); i++)
			System.out.println(actionList.get(i).type);
	}
	
	/**
	 * Counts the number of cleaned tiles in the floor
	 * @return current number of cleaned tiles
	 */
	public int squaresNowCleaned(){
		int cleanedSquare = 0;
		for (int i = 0; i < perception.floor.length; i++)
	        for (int j = 0; j < perception.floor.width; j++)
				if(perception.floor.get(i,j) == Square.Type.CLEAN)
					cleanedSquare++;
		return cleanedSquare;
	}
	/**
	 * Counts the number of dirty tiles in the floor
	 * @return current number of dirty tiles
	 */
	public int dirtySquares(){
		int dirtySquare = 0;
		for (int i = 0; i < perception.floor.length; i++)
	        for (int j = 0; j < perception.floor.width; j++)
				if(perception.floor.get(i,j) == Square.Type.DIRTY)
					dirtySquare++;
		return dirtySquare;
	}
	
	public void updateGoal(){
		switch (visType) {
		case ALL:
			if(dirtySquares()==0){
				System.out.println("GOAL ALL REACHED");
				goalReached = true;
			}
			break;
		case MY_NEIGHBOURS:
			if(actionList.size()>=opBound)
			{
				System.out.println("GOAL My Neighbor");
				goalReached = true;
			}
			break;
		default:
			break;
		}
		
	}
	
	/**
	 * Add the current action to the agent action list
	 * @return current action of the agent
	 */
	public Action.Type action(){
		actionList.add(new Action(currAction, x, y));
		return currAction;
	}
	
	public void setGraphFactory(GraphFactory2 factory)
	{
		this.factory=factory;
	}
}
